﻿
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Configuration
Imports System.Configuration.Provider
Imports System.Data
Imports System.Data.OleDb
Imports System.Globalization
Imports System.Linq
Imports System.Security.Permissions
Imports System.Text

Imports Microsoft.Search.Interop



<Assembly: CLSCompliant(True)> 
<Assembly: OleDbPermission(SecurityAction.RequestMinimum, Unrestricted:=True)> 


Namespace Providers


    ''' <summary>
    ''' Manages storage of picture information using the Windows Search API
    ''' </summary>
    Public Class WdsPictureProvider
        Inherits PictureProvider
        Private Const STR_Description As String = "description"
        Private Const STR_IncludeMyPicturesFolder As String = "includeMyPicturesFolder"
        Private Const STR_CustomFolders As String = "customFolders"
        Private Const STR_ConnectionStringName As String = "connectionStringName"
        Private Const STR_ConnectionStrings As String = "connectionStrings"



        Private connectionString As String

        Private userMyPicturesFolderPath As String



        Private _customFolders As ReadOnlyCollection(Of String)
        Public ReadOnly Property CustomFolders() As ReadOnlyCollection(Of String)
            Get
                If _customFolders Is Nothing Then _customFolders = New ReadOnlyCollection(Of String)(New List(Of String))

                Return _customFolders
            End Get
        End Property




        Private _includeMyPicturesFolder As Boolean
        Public Property IncludeMyPicturesFolder() As Boolean
            Get
                Return _includeMyPicturesFolder
            End Get
            Set(ByVal value As Boolean)
                _includeMyPicturesFolder = value
            End Set
        End Property





        Private _foldersToScope As ReadOnlyCollection(Of String)
        Public ReadOnly Property FoldersToScope() As ReadOnlyCollection(Of String)
            Get
                If _foldersToScope Is Nothing Then
                    Dim list As New List(Of String)

                    If IncludeMyPicturesFolder Then list.Add(userMyPicturesFolderPath)
                    list.AddRange(CustomFolders)

                    _foldersToScope = New ReadOnlyCollection(Of String)(list)

                End If

                Return _foldersToScope
            End Get
        End Property

        Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)


            If config Is Nothing OrElse config.Count = 0 Then Throw New ArgumentNullException("config", "You must supply a valid configuration dictionary.")

            If String.IsNullOrEmpty(config(STR_Description)) Then
                config.Remove(STR_Description)
                config.Add(STR_Description, "Put a localized description here.")
            End If


            'Let ProviderBase perform the basic initialization
            MyBase.Initialize(name, config)

            'Parse the includePicturesFolder attribute
            Dim includePFAttr As String = config(STR_IncludeMyPicturesFolder)

            If String.IsNullOrEmpty(includePFAttr) Then Throw New ProviderException("You must specify the includeMyPicturesFolder attribute.")


            Dim includePF As Boolean
            If Boolean.TryParse(includePFAttr, includePF) Then

                _includeMyPicturesFolder = includePF

                If includePF Then Me.userMyPicturesFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)


            End If

            config.Remove(STR_IncludeMyPicturesFolder)

            'Parse the customFolders attribute
            Dim customFolders = config(STR_CustomFolders)


            If Not String.IsNullOrEmpty(customFolders) Then

                Dim folders = customFolders.Split(";".ToCharArray())
                Me._customFolders = New ReadOnlyCollection(Of String)(folders)

            End If


            config.Remove(STR_CustomFolders)

            'Get the connection string
            Dim connectionStringName = config(STR_ConnectionStringName)
            If String.IsNullOrEmpty(connectionStringName) Then Throw New ProviderException("You must specify a connectionStringName attribute.")

            Dim cs = DirectCast(ConfigurationManager.GetSection(STR_ConnectionStrings), ConnectionStringsSection)

            If cs Is Nothing Then Throw New ProviderException("An error occurred retrieving the connection strings section.")


            If cs.ConnectionStrings(connectionStringName) Is Nothing Then
                Throw New ProviderException("The connection string could not be found in the connection strings section.")
            Else
                connectionString = cs.ConnectionStrings(connectionStringName).ConnectionString
            End If

            If String.IsNullOrEmpty(connectionString) Then Throw New ProviderException("The connection string is invalid.")

            config.Remove(STR_ConnectionStringName)

            'Check to see if unexpected attributes were set in configuration
            If (config.Count > 0) Then

                Dim extraAttribute = config.GetKey(0)
                If Not String.IsNullOrEmpty(extraAttribute) Then
                    Throw New ProviderException(String.Format("The following unrecognized attribute was found in {0}'s configuration: '{1}'", name, extraAttribute))
                Else
                    Throw New ProviderException("An unrecognized attribute was found in the provider's configuration.")
                End If
            End If
        End Sub



        Public Overrides Function GetPictureById(ByVal pictureId As String) As Picture


            Dim parsedId As Decimal
            If Not Decimal.TryParse(pictureId.Substring(0, pictureId.LastIndexOf(".", StringComparison.Ordinal)), parsedId) Then Return Nothing


            Dim queryHelper = GetQueryHelper()
            queryHelper.QueryWhereRestrictions = String.Format("{0} AND System.FileFRN = {1}", queryHelper.QueryWhereRestrictions, parsedId)
            queryHelper.QuerySorting = String.Empty

            Dim cmd As New OleDbCommand()
            cmd.CommandText = queryHelper.GenerateSQLFromUserQuery(" ")

            Dim p As Picture = Nothing

            Using conn = GetOleDbConnection()

                cmd.Connection = conn
                conn.Open()
                Dim rdr = cmd.ExecuteReader()
                If rdr.Read() Then
                    p = PopulatePictureFromIDataReader(rdr)
                End If

            End Using


            Return p

        End Function

        Public Overrides Function GetPictures() As System.Collections.Generic.List(Of Picture)

            Dim queryHelper = GetQueryHelper()

            Dim cmd As New OleDbCommand()
            cmd.CommandText = queryHelper.GenerateSQLFromUserQuery(" ")

            Dim list As New List(Of Picture)
            Using conn = GetOleDbConnection()
                cmd.Connection = conn
                conn.Open()
                Dim rdr = cmd.ExecuteReader()
                While rdr.Read()
                    list.Add(PopulatePictureFromIDataReader(rdr))
                End While
            End Using

            Return list

        End Function

        Public Overrides Function GetPicturesByTag(ByVal tagName As String) As System.Collections.Generic.List(Of Picture)

            Dim queryHelper = GetQueryHelper()

            Dim cmd As New OleDbCommand()
            cmd.CommandText = queryHelper.GenerateSQLFromUserQuery(String.Format("tag:{0}", tagName))

            Dim list As New List(Of Picture)
            Using conn = GetOleDbConnection()
                cmd.Connection = conn
                conn.Open()
                Dim rdr = cmd.ExecuteReader()
                While rdr.Read()
                    list.Add(PopulatePictureFromIDataReader(rdr))
                End While
            End Using

            Return list
        End Function



#Region "Helpers"

        Private Function GetQueryHelper() As CSearchQueryHelper


            Dim manager As New CSearchManager()

            'the SystemIndex catalog is the default catalog that windows uses
            Dim catalogManager = manager.GetCatalog("SystemIndex")

            ' get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer
            Dim queryHelper = catalogManager.GetQueryHelper

            queryHelper.QuerySelectColumns = "System.ItemNameDisplay, System.Title, System.Comment, System.FileName, System.FileAttributes, System.FileExtension, System.MIMEType, System.Keywords, System.ItemUrl, System.Rating, System.RatingText, System.Size, System.FileFRN, System.DateModified"
            queryHelper.QueryWhereRestrictions = "AND Contains(System.ItemType, '.jpg OR .png OR .gif OR .bmp')"
            queryHelper.QuerySorting = "System.DateModified DESC"

            'limit the query to the user-defined folders
            Dim query As New StringBuilder(queryHelper.QueryWhereRestrictions)
            query.Append(" AND (")

            For Each folder In FoldersToScope
                query.AppendFormat("scope='file:{0}'", folder)

                If (FoldersToScope.IndexOf(folder) <> FoldersToScope.Count - 1) Then query.Append(" OR ")

            Next

            query.Append(")")
            queryHelper.QueryWhereRestrictions = query.ToString()

            Return queryHelper
        End Function

        Private Shared Function PopulatePictureFromIDataReader(ByVal rdr As IDataReader) As Picture

            ' For more information see the list of available columns:
            ' http://msdn2.microsoft.com/en-us/library/aa830600.aspx

            Dim p As New Picture()

            Dim rating As Integer = If(rdr("System.Rating") Is DBNull.Value, 0, DirectCast(rdr("System.Rating"), Integer))
            With p

                .Id = Decimal.Parse(rdr("System.FileFRN").ToString()).ToString()
                .FileName = CType(rdr("System.FileName"), String)
                .ItemUrl = New Uri(CType(rdr("System.ItemUrl"), String))

                .FileExtension = rdr("System.FileExtension")
                .Title = If(rdr("System.Title") Is DBNull.Value, CType(rdr("System.ItemNameDisplay"), String), CType(rdr("System.Title"), String))

                .Tags = If(rdr("System.Keywords") Is DBNull.Value, Enumerable.Empty(Of String).ToArray(), CType(rdr("System.Keywords"), String()))
                .Size = Integer.Parse(rdr("System.Size"))

                .MimeType = DirectCast(rdr("System.MIMEType"), String)

                .DateModified = DirectCast(rdr("System.DateModified"), DateTimeOffset)

                .Comment = If(rdr("System.Comment") Is DBNull.Value, String.Empty, DirectCast(rdr("System.Comment"), String))





                ' Rating comes in a special format, please reffer to the following link for more information:
                ' http://msdn2.microsoft.com/en-us/library/aa966528.aspx

                Select Case rating

                    Case 0 To 12
                        .Rating = 1
                    Case 13 To 37
                        .Rating = 2
                    Case 38 To 62
                        .Rating = 3
                    Case 63 To 87
                        .Rating = 4
                    Case 88 To 99
                        .Rating = 5
                    Case Else
                        .Rating = 0

                End Select
            End With



            Return p

        End Function


        Private Function GetOleDbConnection() As OleDbConnection
            Try
                Return New OleDbConnection(Me.connectionString)
            Catch
                Throw
            End Try
        End Function



#End Region


    End Class


End Namespace
